/*  
  -- The Lime II Project -- 

  A tuplespaces-based middleware for coordinating agents and mobile hosts.
  Copyright (C) 2005.
  Gian Pietro Picco, Amy L. Murphy, Lorenzo Bellini.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  Lesser General Public License for more details.

  You should have received this copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
 */

package lime2;

import sagroup.dracots.DracoTuple;
import sagroup.dracots.ShareTuple;

import sagroup.dracots.TupleTypeInfo;
import lights.interfaces.*;

class OriginalLimeTSMgr extends LimeTSMgr {
	// the (proxied) tuplespace
	private ITupleSpace ts;

	// the reactor
	private Reactor reactor;

	/**
	 * Constructor method.
	 * 
	 * @param name
	 *            the name of the tuplespace
	 * @param policy
	 *            for the tuplespace
	 */
	OriginalLimeTSMgr(String name, byte policy) {
		// creates the (proxied) tuplespace
		try {
			ts = new TupleSpaceProxy(name);
		} catch (TupleSpaceException tse) {
			throw new LimeError("tuplespace engine creation error", tse);
		}
		// creates the reactor
		reactor = new Reactor(ts, strong);
		// registers the name
		this.name = name;
		// registers the policy
		this.policy = policy;
		// registers itself
		ltsms.put(name, this);
	}

	/**
	 * Registers strong reactions.
	 * 
	 * @param r
	 *            array of strong reactions to install
	 * @param ids
	 *            array of the corresponding reaction identifiers
	 * @return the (same) reaction identifiers
	 */
	protected synchronized ReactionID[] registerStrong(Reaction[] r,
			ReactionID[] ids) {
		// previous parameter checking guarantees r not to be null
		// LocalOpMgr guarantees ids to be a valid array of ids (not null, same
		// length of r, etc..)
		LinkedRegisteredReaction[] rr = new LinkedRegisteredReaction[r.length];
		for (int i = 0; i < r.length; i++) {
			rr[i] = new LinkedRegisteredReaction(r[i], ids[i]);
			strong.put(ids[i], rr[i]);
		}
		reactor.put(rr);
		if (!reactor.isRunning()) {
			if (!weak_running)
				reactor.runReactiveProgram(null);
			else
				interrupt = true;
		}
		return ids;
	}

	/**
	 * Deregisters strong reactions.
	 * 
	 * @param ids
	 *            identifiers of the strong reactions to deregister
	 * @return boolean[], where <code>boolean[i]</code> is <code>true</code> if
	 *         reaction <code>i</code> was successfully deregistered,
	 *         <code>false</code> otherwise
	 */
	protected synchronized boolean[] deregisterStrong(ReactionID[] ids) {
		// previous parameter checking guarantees ids not to be null
		return reactor.remove(ids);
	}

	/**
	 * Fires a weak reaction.
	 * 
	 * @param id
	 *            identifier of the weak reaction
	 * @param re
	 *            reaction event that has to be used to fire the weak reaction
	 */
	protected synchronized void fireWeak(ReactionID id, ReactionEvent re) {
		RegisteredReaction wr = (RegisteredReaction) weak.get(id);
		// the weak part of the weak reaction is still registered ?
		if (wr != null)
			// also the weak part of a once-per-tuple weak reaction keeps track
			// of the tuples it has already reacted to
			if ((wr.getMode() == Reaction.ONCE)
					|| (!wr.alreadyReactedTo(LimeWrapper.getID(re.getTuple())))) {
				// executes the "reactsTo" method
				weak_running = true;
				wr.getListener().reactsToOut(re);
				weak_running = false;
				// if the weak reaction is a ONCE reaction then we must
				// uninstall it
				if (wr.getMode() == Reaction.ONCE)
					weak.remove(id);
				// we have to run the reactor if there was (at least one)
				// attempt to break atomicity of the weak reaction
				if (interrupt) {
					interrupt = false;
					reactor.runReactiveProgram(null);
				}
			}
	}

	/**
	 * Returns whether we are within the execution of (the listener of) a strong
	 * or weak reaction.
	 * 
	 * @return boolean
	 */
	protected boolean isWithinReaction() {
		return weak_running || reactor.isRunning();
	}

	/**
	 * Writes an array of tuples into the reactive tuplespace.
	 * 
	 * @param t
	 *            array of tuples to write
	 */
	protected synchronized void doWrite(ITuple[] t) {
		if (!reactor.isRunning()) {
			if (!weak_running)
				reactor.runReactiveProgram(t);
			else {
				interrupt = true;
				reactor.writeWorkingTuples(t);
			}
		} else
			reactor.writeNewTuples(t);

		if (t != null)
			for (ITuple it : t) {
				recordOut(it);
			}
	}

	private void recordOut(ITuple it) {
		ShareTuple st = restoreShareTuple(it);
		if(st !=null && st.isMaster())
		{
			TupleTypeInfo.getTypeRecord(name, st.getType())
					.incrOutCount(1);
			//System.out.println(">>>" + st.getType() + " type out once.");
		}

	}

	/**
	 * Delegates to: Reactor.doRead (.. only if we are within a reaction !)
	 * 
	 * @param t
	 *            ITuple
	 * @return ITuple
	 */
	protected synchronized ITuple doRead(ITuple t) {
		if (!isWithinReaction()) {
			ITuple result = null;
			try {
				result = ts.rdp(t);
			} catch (TupleSpaceException tse) {
				throw new LimeRuntimeException("tuplespace engine error", tse);
			}
			return result;
		}
		return reactor.doRead(t);
	}

	/**
	 * Delegates to: Reactor.doReadg (.. only if we are within a reaction !)
	 * 
	 * @param t
	 *            ITuple
	 * @return array of ITuples
	 */
	protected synchronized ITuple[] doReadg(ITuple t) {
		if (!isWithinReaction()) {
			ITuple[] results = null;
			try {
				results = ts.rdg(t);
			} catch (TupleSpaceException tse) {
				throw new LimeRuntimeException("tuplespace engine error", tse);
			}
			return results;
		}
		return reactor.doReadg(t);
	}

	/**
	 * Delegates to: Reactor.doTake (.. only if we are within a reaction !)
	 * 
	 * @param t
	 *            ITuple
	 * @return ITuple
	 */
	protected synchronized ITuple doTake(ITuple t) {

		ITuple result = null;
		if (!isWithinReaction()) {

			try {
				result = ts.inp(t);
			} catch (TupleSpaceException tse) {
				throw new LimeRuntimeException("tuplespace engine error", tse);
			}

		} else
			result = reactor.doTake(t);
		recordTake(t);
		return result;
	}

	/**
	 * Delegates to: Reactor.doTakeg (.. only if we are within a reaction !)
	 * 
	 * @param t
	 *            ITuple
	 * @return array of ITuples
	 */
	protected synchronized ITuple[] doTakeg(ITuple t) {

		ITuple[] results = null;
		if (!isWithinReaction()) {

			try {
				results = ts.ing(t);
			} catch (TupleSpaceException tse) {
				throw new LimeRuntimeException("tuplespace engine error", tse);
			}

		} else
			results = reactor.doTakeg(t);
		if (results != null)
			for (ITuple it : results) {
				recordTake(it);
			}
		return results;
	}

	private ShareTuple restoreShareTuple(ITuple it) {
		ShareTuple st = null;

		if (it != null) {
			try {
				DracoTuple dt = DracoTuple.create(it);
				if (dt instanceof ShareTuple) {
					st = (ShareTuple) dt;
				}
			} catch (ClassNotFoundException ce) {

			}catch(ArrayIndexOutOfBoundsException ae)
			{
				//System.out.println("array out of happend "+ it);
			}catch(Exception ex)
			{
				
			}

		}
		return st;
	}

	private void recordTake(ITuple it) {
		ShareTuple st = restoreShareTuple(it);
		if (st != null && st.isMaster())
		{
			TupleTypeInfo.getTypeRecord(name, st.getType()).incrTakeCount(1);
		}
	}

	protected synchronized ITuple doUpdate(ITuple template, ITuple it) {
		ITuple ret = null;

		if (!isWithinReaction()) {

			try {
				ret = ts.inp(template);
			} catch (TupleSpaceException tse) {
				throw new LimeRuntimeException("tuplespace engine error", tse);
			}
		} else {
			ret = reactor.doTake(template);
		}

		ITuple[] t = new ITuple[] { it };
		if (ret != null) {
			if (!reactor.isRunning()) {
				if (!weak_running)
					reactor.runReactiveProgram(t);
				else {
					interrupt = true;
					reactor.writeWorkingTuples(t);
				}
			} else
				reactor.writeNewTuples(t);
			recordUpdate(it);
		}

		return ret;
	}

	private void recordUpdate(ITuple it) {
		ShareTuple st = restoreShareTuple(it);
		if (st != null && st.isMaster()) {
			TupleTypeInfo.getTypeRecord(name, st.getType()).incrUpdateCount(1);
		}
	}

}